
package ib_cluster.networks;

import ib_model.src.*;
import ib_cluster.network_module.channels.*;
import ib_cluster.network_module.control.*;
import ib_cluster.network_module.computation.*;

network ib_3_level
{
	parameters:
        // it is suggested that the numMachine should be a multiple of the number of lowest level switch
		int numMachine = default(32); // the num of machines should be a multiple of the num for l1 switch

        // it is suggested that the number of switch for each level should be a multiple of it's corresponding upper level switch 
		// the number of switch for all layers except highest layer should be even number
		int numL1switch = default(8);
		int numL2switch = default(int(numL1switch/2));
        int numL3switch = default(int(numL2switch/2));

		int numGpusperMachine = default(8);
		int switch_delay = default(0);
	
	submodules:
		machine[numMachine]: Machine 
		{
		    parameters:
		        @display("i=device/server2;p=80,109");
		}
	    
		taskgenerator: TaskGenerator
		{
		    parameters:
		        maxnumGpus = numGpusperMachine * numMachine;
		}

		centralcontroller: CentralController 
		{
		    parameters:
		        numMachine = numMachine;
		        numSwitch = numL1switch + numL2switch + numL3switch;
		        numGpusperMachine = numGpusperMachine;
		    gates:
		        machine_port[numMachine];
		        switch_port[numL1switch + numL2switch + numL3switch];
		}

		topo_manager : TopoManager 
		{

		}

		sw_l1[numL1switch]: Switch 
		{
			parameters:
				numSwitchPorts = int(numMachine/numL1switch)*numGpusperMachine + 1;
				@display("p=182,58");
			gates:
				port[int(numMachine/numL1switch)*numGpusperMachine + 1];
		}

		sw_l2[numL2switch]: Switch 
		{
			parameters:
				numSwitchPorts = int(numL1switch/numL2switch)+numL3switch;
                is_cross_connected = true;
				cross_connected_upperlevel_numswitch = numL3switch;
				@display("p=182,58");
			gates:
				port[int(numL1switch/numL2switch)+numL3switch];
		}

        sw_l3[numL3switch]: Switch 
		{
			parameters:
				numSwitchPorts = numL2switch;
				@display("p=182,58");
			gates:
				port[numL2switch];
		}

		virtual_switch: IB_Virtual_Data_Switch 
		{
		    parameters:
		        numSwitchPorts = numMachine*numGpusperMachine;
		    gates:
		        port[numMachine*numGpusperMachine];
        	}

	connections allowunconnected:
	
		// Connect the task generater and centralcontroller
        	taskgenerator.central_controller_port --> centralcontroller.task_generater_port;
	
		// Connect every machines to its respective L1 switch, central controller and virtual data switch
		for j = 0..numMachine-1
		{
		    machine[j].controller_port <--> ControlPlane <--> centralcontroller.machine_port[j];
		}

		// Connect the machines to L1 switches and virtual data switch
		for i=0..numMachine-1, for j=0..numGpusperMachine-1 
		{
		    virtual_switch.port[i*numGpusperMachine+j] <--> ControlPlane <-->machine[i].virtual_data_switch_port[j];
		    machine[i].out_port[j] <--> IB4XQDRWire <--> sw_l1[int(i/int(numMachine/numL1switch))].port[(j+i*numGpusperMachine) % (int(numMachine/numL1switch)*numGpusperMachine)];
		}
	
		// Connect the L1 switches to the central controller
		for i = 0..numL1switch-1
		{
		    sw_l1[i].central_controller_port <--> ControlPlane <--> centralcontroller.switch_port[i];
		}

		// Connect the L2 switches to the central controller
		for i = 0..numL2switch-1
		{
		    sw_l2[i].central_controller_port <--> ControlPlane <--> centralcontroller.switch_port[i + numL1switch];
		}

		// Connect the L3 switches to the central controller
		for i = 0..numL3switch-1
		{
		    sw_l3[i].central_controller_port <--> ControlPlane <--> centralcontroller.switch_port[i+ numL1switch + numL2switch];
		}
		
		// Connect the L1 switches to the L2 switches
		//for i = 0..numL1switch-1, for j =0..numL2switch-1
		//{
		//	sw_l1[i].port[1 + int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[j].port[i % int(numL1switch/numL2switch)];
		//}

		sw_l1[0].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[0].port[0];
		sw_l1[1].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[0].port[1];
		sw_l1[2].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[1].port[0];
		sw_l1[3].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[1].port[1];
		sw_l1[4].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[2].port[0];
		sw_l1[5].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[2].port[1];
		sw_l1[6].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[3].port[0];
		sw_l1[7].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[3].port[1];
		//sw_l1[8].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[4].port[0];
		//sw_l1[9].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[4].port[1];
		//sw_l1[10].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[5].port[0];
		//sw_l1[11].port[int(numMachine/numL1switch)*numGpusperMachine] <--> IB4XQDRWire <--> sw_l2[5].port[1];


		// Connect the L2 switches to the L3 switches
		for i = 0..numL2switch-1, for j =0..numL3switch-1
		{
			sw_l2[i].port[j + int(numL1switch/numL2switch)] <--> IB4XQDRWire <--> sw_l3[j].port[i];
		}
}